package net.cyndeline.GUI.input
import net.cyndeline.entitySystem.EntityRef
import net.cyndeline.entitySystem.Event
import net.cyndeline.events.movement.MovementEvent
import com.escalatesoft.subcut.inject.BindingModule
import com.escalatesoft.subcut.inject.Injectable
import net.cyndeline.events.movement.Movement
import net.cyndeline.components.world.PositionComponent
import net.cyndeline.GUI.PlayerEngineContainer
import net.cyndeline.GUI.PlayerGUIContainer
import net.cyndeline.components.world.PositionComponent
import net.cyndeline.subcut.ProjectConfiguration
import net.cyndeline.components.world.Position
import language.postfixOps

/**
 * Used to translate user inputs to something the engine can make use of.
 * 
 * This object implements two interfaces, 
 */
class PlayerEntityContainer(implicit val bindingModule: BindingModule) 
		extends PlayerGUIContainer with PlayerEngineContainer with Injectable {
  private var playerEntity: Option[EntityRef] = None
  
  /* If false, will prevent events from being sent. */
  private var inputEnabled = false
  
  override def move(x: Int, y: Int): Boolean = {
    playerEntity match {
      case Some(player) => {
        val playerPosition = player.getComponent(classOf[Position]).get
        val position = (playerPosition.x, playerPosition.y)
        val move = injectOptional[MovementEvent] getOrElse new Movement()
        move.start = position
        move.stop = (position._1 + x, position._2 + y)
        send(move)
      }
      case None => false
    }
  }
  
  override def player: Option[EntityRef] = playerEntity
  
  override def player_=(entity: EntityRef) = synchronized {
    playerEntity = Option(entity)
  }
  
  override def enableInput() = inputEnabled = true
  
  override def copy: PlayerEngineContainer = {
    val cpy = new PlayerEntityContainer()
    if (playerEntity isDefined) {
      cpy.player = playerEntity get
    }
    if (inputEnabled) {
      cpy.enableInput()
    }
    
    cpy
  }
  
  private def send(event: Event): Boolean = synchronized {
    if (inputEnabled) {
	    playerEntity match {
	      case Some(player) => {
	        player send event
	        
	        /* Only one input is allowed until the engine says otherwise. */
	        inputEnabled = false
	        true
	      }
	      case None => false
	    }
  	} else {
  	  false
  	}
  }
}

/**
 * Handles the creation of a single unique instance for the dependency 
 * injection framework.
 */
object PlayerEntityContainer {
  private var guiInput: Option[PlayerEntityContainer] = None
  
  def make: PlayerEntityContainer = {
    implicit val bindingModule = ProjectConfiguration
    
    if (guiInput.isDefined) {
      guiInput.get
      
    } else {
      /* This is needed since the injection framework can't bind a single
       * instance to two interfaces. 
       */
      guiInput = Option(new PlayerEntityContainer())
      guiInput.get
    }
  }
}